#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h> 
#include <stdlib.h>       
#include <string.h>
#include <pthread.h>
//#include <sys/sem.h>
#include <errno.h>
#include <sys/ipc.h>
//#include <sys/msg.h>
#include <sys/timeb.h>
#include <sys/socket.h>
//#include <sys/socketvar.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <semaphore.h>
#include <string.h>
#include <termios.h>
#include <linux/input.h>
#include <semaphore.h>
#include <linux/shm.h>
#include <asm/unistd.h>

#include "common.h"

static int sock = -1;
static struct sockaddr_in addr;	


#define HEART_BEAT_MAX_TIME 	5

//protoSlaveType
enum _protoSlaveType
{
		_askForPush				 			= 	0x00,
		_onLineHeartBeat			 	= 	0x01,
		_stopPush								= 	0x02,
		_sendOneFrameData				=		0x03,
};

typedef struct _contrlData
{
		pthread_mutex_t Lock;
		unsigned int serviceNumber;
		struct sockaddr_in clientAddr;
		unsigned int clientLastHeartBeatTime;
}ContrlData;
static ContrlData *pContrlData = NULL;

static int askForPush(struct recvData *recvData, struct packageFilter *pFilter);
static int onLineHeartBeat(struct recvData *recvData, struct packageFilter *pFilter);
static int stopPush(struct recvData *recvData, struct packageFilter *pFilter);
static int sendOneFrameData(struct recvData *recvData, struct packageFilter *pFilter);

static void* checkClientHeartBeatThread(void *arg);

int initPushVideoServer()
{
		pContrlData = (ContrlData*)malloc(sizeof(ContrlData));
		if(pContrlData == NULL)
		{
				printf("pContrlData == NULL ! {%s(%d)}\n",__FILE__,__LINE__);
				return -1;	
		}
		
		memset(pContrlData, 0, sizeof(ContrlData));
		pContrlData->serviceNumber = 0;
		pContrlData->clientAddr.sin_family = AF_INET;
		
		if( pthread_mutex_init (&(pContrlData->Lock),NULL) != 0   ) 
		{
				printf("pthread_mutex_init failure {%s(%d)}\n",__FILE__,__LINE__);	
				free(pContrlData);
				pContrlData = NULL;
				return -2;
		}	
	
		pthread_t ptID;	
		if( pthread_create(&ptID, NULL, checkClientHeartBeatThread, (void *)0) != 0 )
		{
				printf("create thread failure {%s(%d)}\n",__FILE__,__LINE__);		
				return -3;
		}  
}

void pushVideoServer(struct recvData *recvData)
{								
		if(recvData == NULL)
		{
				printf("recvData == NULL ! {%s(%d)}\n",__FILE__,__LINE__);
				return;	
		}
	
		if(pContrlData == NULL)
		{
				printf("pContrlData == NULL ! {%s(%d)}\n",__FILE__,__LINE__);
				return;	
		}
		
		if( sock == -1 )
   	{
		   	sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);		
				addr.sin_family     = AF_INET;
				addr.sin_addr.s_addr = inet_addr( "127.0.0.1" );
				//addr.sin_addr.s_addr = inet_addr( "192.168.100.177" ); //for test
				addr.sin_port       = htons( 8904 );
				
				if( sock == -1 ) 
				{
						printf("bind 8904 fail ! {%s,%d}\n", __FILE__, __LINE__);
						return;
				}
		}
	
		struct packageFilter filter;
		int ret = explainPackageHead(recvData, &filter);
		if(ret < 0)
		{
				return;	
		}
		
		switch(filter.protoSlaveType)
		{
				case _askForPush:
				{
						askForPush(recvData, &filter);
						break;	
				}
				case _onLineHeartBeat:
				{
						onLineHeartBeat(recvData, &filter);
						break;
				}
				case _stopPush:
				{
						stopPush(recvData, &filter);
						break;	
				}
				case _sendOneFrameData:
				{
						sendOneFrameData(recvData, &filter);
						break;
				}
				default:
				{
						printf("filter.protoSlaveType not right !  filter.protoSlaveType = %d\n", filter.protoSlaveType);
						break;	
				}
		}
}


static int askForPush(struct recvData *recvData, struct packageFilter *pFilter)
{
		printf("into askForPush\n");
		if(pFilter->dataDirection != 0x00)	
		{
				printf("proto 5,0 pFilter->dataDirection error ! pFilter->dataDirection = %d {%s,%d}\n", pFilter->dataDirection, __FILE__, __LINE__);
				printf("recvData->buf[3], buf[4] = %d,%d\n", recvData->buf[3], recvData->buf[4]);
				return -1;	
		}
		
		if(pFilter->dataLen != 5)
		{
				printf("proto 5,0 pFilter->dataLen error ! pFilter->dataLen = %d\n", pFilter->dataLen);
				return -2;	
		}
		
		unsigned char videoType = pFilter->pData[0];
		unsigned char privateData[4];
		memcpy(privateData, pFilter->pData + 1, 4);
		unsigned char returnRet;
		unsigned int serviceNumber;
		
		pthread_mutex_lock (&(pContrlData->Lock));
		if(pContrlData->clientAddr.sin_addr.s_addr == 0 || pContrlData->clientAddr.sin_addr.s_addr == recvData->addr.sin_addr.s_addr)
		{
				pContrlData->serviceNumber ++;
				pContrlData->clientLastHeartBeatTime = time(NULL);
				pContrlData->clientAddr.sin_addr.s_addr = recvData->addr.sin_addr.s_addr;
				pContrlData->clientAddr.sin_port = recvData->addr.sin_port;
				
				returnRet = 0;
				serviceNumber = pContrlData->serviceNumber;
				
				printf("ask for push success ! serviceNumber=%d\n", serviceNumber);
				printf("pContrlData->clientAddr.sin_addr.s_addr, pContrlData->clientAddr.sin_port=%d,%d\n", pContrlData->clientAddr.sin_addr.s_addr, pContrlData->clientAddr.sin_port);
				printf("recvData->addr.sin_addr.s_addr, recvData->addr.sin_port=%d,%d\n", recvData->addr.sin_addr.s_addr, recvData->addr.sin_port);
		}
		else
		{
				printf("ask for push fail ! serviceNumber=%d\n", serviceNumber);
				printf("pContrlData->clientAddr.sin_addr.s_addr, pContrlData->clientAddr.sin_port=%d,%d\n", pContrlData->clientAddr.sin_addr.s_addr, pContrlData->clientAddr.sin_port);
				printf("recvData->addr.sin_addr.s_addr, recvData->addr.sin_port=%d,%d\n", recvData->addr.sin_addr.s_addr, recvData->addr.sin_port);
				returnRet = 1;	
				serviceNumber = pContrlData->serviceNumber;
		}
		pthread_mutex_unlock (&(pContrlData->Lock));
		
		unsigned char dataDirection = 0x01;
		unsigned short dataLen = 10;
		unsigned int headLen = recvData->count - pFilter->dataLen;
		unsigned int packageSize = headLen + dataLen;
		createProtoCombinationData(recvData->buf + 3, pFilter->deviceType, dataDirection, pFilter->communicationWay, dataLen);
		
		unsigned char checkCode = 0;
		for(unsigned char i=0; i < headLen-1; i++)
		{
				checkCode += recvData->buf[i];
		}
		recvData->buf[headLen - 1] = checkCode;
		
		recvData->buf[headLen] = videoType;
		recvData->buf[headLen + 1] = returnRet;
		memcpy(recvData->buf + headLen + 2, privateData, 4);
		changeUIntToByte(serviceNumber, recvData->buf + headLen + 6);
		
		MainSend(recvData->buf, packageSize, &(recvData->addr));
		
		return 0;
}


static int onLineHeartBeat(struct recvData *recvData, struct packageFilter *pFilter)
{
		if(pFilter->dataDirection != 0x00)	
		{
				printf("proto 5,1 pFilter->dataDirection error ! pFilter->dataDirection = %d\n", pFilter->dataDirection);
				return -1;	
		}
		
		if(pFilter->dataLen != 17)
		{
				printf("proto 5,1 pFilter->dataLen error ! pFilter->dataLen = %d\n", pFilter->dataLen);
				return -2;	
		}
	
		if(&(recvData->addr) == NULL)
		{
				printf("recvData->addr == NULL !\n");
				return -3;	
		}
		
		int ret;
		unsigned char privateData[4];
		unsigned int serviceNumber;
		memcpy(privateData, pFilter->pData + 9, 4);
		changeByteToUInt(serviceNumber, pFilter->pData + 13);

		pthread_mutex_lock (&(pContrlData->Lock));
		if(pContrlData->serviceNumber != serviceNumber)
		{
				//printf("onLineHeartBeat:serviceNumber not match ! pContrlData->serviceNumber=%d,serviceNumber=%d {%s,%d}\n",pContrlData->serviceNumber,serviceNumber,__FILE__,__LINE__);	
		}
		else if(pContrlData->clientAddr.sin_addr.s_addr != recvData->addr.sin_addr.s_addr)
		{
				//printf("onLineHeartBeat:ipaddr not match ! saveAddr=%d,nowAddr=%d {%s,%d}\n",pContrlData->clientAddr.sin_addr.s_addr,recvData->addr.sin_addr.s_addr,__FILE__,__LINE__);	
		}
		else
		{
				pContrlData->clientLastHeartBeatTime = time(NULL);
				//printf("refresh heartBeat Time: time=%d\n", pContrlData->clientLastHeartBeatTime);
		}
		pthread_mutex_unlock (&(pContrlData->Lock));
		
		return 0;
		
}

static int stopPush(struct recvData *recvData, struct packageFilter *pFilter)
{
		if(pFilter->dataDirection != 0x00)	
		{
				printf("proto 5,1 pFilter->dataDirection error ! pFilter->dataDirection = %d\n", pFilter->dataDirection);
				return -1;	
		}
		
		if(pFilter->dataLen != 17)
		{
				printf("proto 5,1 pFilter->dataLen error ! pFilter->dataLen = %d\n", pFilter->dataLen);
				return -2;	
		}
	
		if(&(recvData->addr) == NULL)
		{
				printf("recvData->addr == NULL !\n");
				return -3;	
		}
		
		int ret;
		unsigned char privateData[4];
		unsigned int serviceNumber;
		memcpy(privateData, pFilter->pData + 9, 4);
		changeByteToUInt(serviceNumber, pFilter->pData + 13);

		pthread_mutex_lock (&(pContrlData->Lock));
		if(pContrlData->serviceNumber != serviceNumber)
		{
				printf("stopPush:serviceNumber not match ! pContrlData->serviceNumber=%d,serviceNumber=%d {%s,%d}\n",pContrlData->serviceNumber,serviceNumber,__FILE__,__LINE__);	
		}
		else if(pContrlData->clientAddr.sin_addr.s_addr != recvData->addr.sin_addr.s_addr)
		{
				printf("stopPush:ipaddr not match ! saveAddr=%d,nowAddr=%d {%s,%d}\n",pContrlData->clientAddr.sin_addr.s_addr,recvData->addr.sin_addr.s_addr,__FILE__,__LINE__);	
		}
		else
		{
				pContrlData->clientAddr.sin_addr.s_addr = 0;
				printf("clear pushVideo ipAddr\n");
		}
		pthread_mutex_unlock (&(pContrlData->Lock));
		
		//printf("recv stopPush data success ! {%s,%d}\n", __FILE__,__LINE__);
		return 0;
}

static int sendOneFrameData(struct recvData *recvData, struct packageFilter *pFilter)
{
		int ret;
		socklen_t svrlen = sizeof(struct sockaddr_in);
		
		if(pFilter->dataDirection != 0x00)	
		{
				printf("proto 5,3 pFilter->dataDirection error ! pFilter->dataDirection = %d\n", pFilter->dataDirection);
				return -1;	
		}
		
		if(pFilter->dataLen < 21)
		{
				printf("proto 5,3 pFilter->dataLen error ! pFilter->dataLen = %d\n", pFilter->dataLen);
				return -2;	
		}
	
		if(&(recvData->addr) == NULL)
		{
				printf("recvData->addr == NULL !\n");
				return -3;	
		}
		
		unsigned int serviceNumber;
		changeByteToUInt(serviceNumber, pFilter->pData);
		
		bool canTransit = false;
		pthread_mutex_lock (&(pContrlData->Lock));
		if(pContrlData->serviceNumber != serviceNumber)
		{
				//printf("sendOneFrameData:serviceNumber not match ! pContrlData->serviceNumber=%d,serviceNumber=%d {%s,%d}\n",pContrlData->serviceNumber,serviceNumber,__FILE__,__LINE__);	
		}
		else if(pContrlData->clientAddr.sin_addr.s_addr != recvData->addr.sin_addr.s_addr)
		{
				//printf("sendOneFrameData:ipaddr not match ! saveAddr=%d,nowAddr=%d {%s,%d}\n",pContrlData->clientAddr.sin_addr.s_addr,recvData->addr.sin_addr.s_addr,__FILE__,__LINE__);	
		}
		else
		{
				//printf("prepare to proxy frame data ! recvData->count = %d {%s,%d}\n", recvData->count, __FILE__,__LINE__);
				canTransit = true;
		}
		pthread_mutex_unlock (&(pContrlData->Lock));			
		
		canTransit = true; //for test
		
		if(canTransit == true)
		{
				ret = sendto(sock,recvData->buf,recvData->count,0,(const sockaddr*)&addr,svrlen);
				//printf("proxy frame data len = %d {%s,%d}\n", ret, __FILE__,__LINE__);
		}				
}

static void* checkClientHeartBeatThread(void *arg)
{
		struct timespec ts;
		struct timeval  tv;	
		static sem_t sem_tmp;
		if( sem_init(&(sem_tmp), 1, 0) != 0   ) 
		{
				printf("sem_init failure {%s(%d)}\n",__FILE__,__LINE__);	
				return NULL;
		}	
		
		while( !IsExit() )
		{
				gettimeofday(&tv, NULL); 
				
			  ts.tv_sec  = tv.tv_sec + HEART_BEAT_MAX_TIME;
		    ts.tv_nsec = tv.tv_usec + 0 * 1000 * 1000;		
		    
		    sem_timedwait(&(sem_tmp),&ts);
		    
		    //printf("into checkClientHeartBeatThread !");
		    
		    time_t nowTime = time(NULL);
		    pthread_mutex_lock (&(pContrlData->Lock));  
				if(pContrlData->clientAddr.sin_addr.s_addr != 0)
				{
						if(nowTime - pContrlData->clientLastHeartBeatTime > HEART_BEAT_MAX_TIME)
						{
								printf("clear client info ! nowTime=%ld, clientLastHeartBeatTime=%d {%s,%d}\n", nowTime,pContrlData->clientLastHeartBeatTime, __FILE__, __LINE__);
								printf("pContrlData->clientAddr.sin_addr.s_addr = %d\n", pContrlData->clientAddr.sin_addr.s_addr);
								pContrlData->clientAddr.sin_addr.s_addr = 0;
						}
				}
				pthread_mutex_unlock (&(pContrlData->Lock));
		}
}